package slimgo

import (
	"bytes"
	"errors"
	"github.com/jesusslim/slimgo/context"
	"io/ioutil"
	"strings"
)

type ControllerInterface interface {
	Init(ctx *context.Context, controllerName, actionName string)
	Pre()
	URLMapping()
	Finish()
	HandlerFunc(funcName string) bool
	Render() error
}

type Controller struct {
	Context          *context.Context
	Data             map[interface{}]interface{}
	controllerName   string
	actionName       string
	funcMapping      map[string]func()
	view             string
	controllerNameAb string //if controllerName is TestController,controllerNameAb is Test
}

func (this *Controller) Init(ctx *context.Context, controllerName, actionName string) {
	this.Context = ctx
	this.Data = make(map[interface{}]interface{})
	this.controllerName = controllerName
	this.actionName = actionName
	this.funcMapping = make(map[string]func())
	this.view = ""
	this.controllerNameAb = strings.TrimSuffix(controllerName, "Controller")
}

func (this *Controller) Pre() {

}

func (this *Controller) URLMapping() {

}

func (this *Controller) Finish() {

}

//call func
func (this *Controller) HandlerFunc(funcName string) bool {
	if theFunc, ok := this.funcMapping[funcName]; ok {
		theFunc()
		return true
	} else {
		return false
	}
}

func (this *Controller) Render() error {
	rb, err := this.RenderBytes()
	if err != nil {
		return err
	}
	this.Context.Output.Header("Content-Type", "text/html; charset=utf-8")
	this.Context.Output.Body(rb)
	return nil
}

/********** **********/

func (this *Controller) SetView(view string) {
	this.view = view
}

func (this *Controller) RenderBytes() ([]byte, error) {
	if this.view == "" {
		this.view = this.controllerNameAb + "/" + this.actionName + ".html"
	}
	viewLower := strings.ToLower(this.view)
	if tpl, ok := Tpls[viewLower]; !ok {
		panic("Template not found.")
		return nil, nil
	} else {
		buff := bytes.NewBufferString("")
		err := tpl.tpl.ExecuteTemplate(buff, tpl.file, this.Data)
		if err != nil {
			Error("Template execute failed,", err.Error())
			return nil, err
		}
		content, _ := ioutil.ReadAll(buff)
		return content, nil
	}
}

//mapping method to a map for router
func (this *Controller) Mapping(funcName string, theFunc func()) {
	this.funcMapping[funcName] = theFunc
}

func (this *Controller) Redirect(url string, status int) {
	this.Context.Redirect(url, status)
}

//json
func (this *Controller) ServeJson(data ...interface{}) {
	hasIntent := true
	if len(data) > 0 && data[0] != nil {
		this.Context.Output.Json(data[0], hasIntent)
	} else {
		this.Context.Output.Json(this.Data["json"], hasIntent)
	}
}

//jsonp
func (this *Controller) ServeJsonp(data ...interface{}) {
	hasIntent := true
	if len(data) > 0 && data[0] != nil {
		this.Context.Output.Jsonp(data[0], hasIntent)
	} else {
		this.Context.Output.Jsonp(this.Data["json"], hasIntent)
	}
}

//xml
func (this *Controller) ServeXml(data ...interface{}) {
	hasIntent := true
	if len(data) > 0 && data[0] != nil {
		this.Context.Output.Xml(data[0], hasIntent)
	} else {
		this.Context.Output.Xml(this.Data["xml"], hasIntent)
	}
}

//default index
func (this *Controller) Index() {
	this.Data["json"] = "Default page of " + AppName
	this.ServeJson(nil)
}

/*** session ***/

func (this *Controller) GetSession(key interface{}) (interface{}, error) {
	if this.Context.Input.Session == nil {
		return nil, errors.New("No session set.")
	}
	return this.Context.Input.Session.Get(key), nil
}

func (this *Controller) SetSession(key interface{}, value interface{}) error {
	if this.Context.Input.Session == nil {
		return errors.New("No session set.")
	}
	return this.Context.Input.Session.Set(key, value)
}

func (this *Controller) Session(key interface{}, value ...interface{}) (interface{}, error) {
	if len(value) > 0 {
		return nil, this.SetSession(key, value[0])
	} else {
		return this.GetSession(key)
	}
}

func (this *Controller) DeleteSession(key interface{}) error {
	if this.Context.Input.Session == nil {
		return errors.New("No session set.")
	}
	return this.Context.Input.Session.Delete(key)
}

func (this *Controller) DestorySession() {
	this.Context.Input.Session.SessionClear()
}
